#include "Parser.h"
#include "LightFile.h"
#include "LightFileStorageManager.h"
#include "TagList.h"
#include "ShapeDef.h"
#include "SpriteDef.h"
#include "SceneObject.h"
#include "TimelineObject.h"

#include "zlib.h"
#include "Bitmap.h"
#include "ActionRecord.h"
#include "CStrTokenIterator.h"
#include <time.h>


namespace LightPlayer {


	void Parser::firstUpdate(){
		ActionList& actionList = *(getActionList());
		DisplayList& displayList = *(getDisplayList());

		// step 2 : parse next frame and fill up list of actions
		if( isPlaying() ){
			ParseNextFrame();
		}

		// step 2 : do list of action
		doActionList();
	}

	void Parser::update( float dt ){
		ActionList& actionList = *(getActionList());
		DisplayList& displayList = *(getDisplayList());
		
		// step 2 : parse next frame and fill up list of actions
		if( isPlaying() ){
			ParseNextFrame();
		}

		// step 2 : do list of action
		doActionList();
		
		display_list_type::iterator it = displayList.list.begin();
		for (; it != displayList.list.end(); ++it ){
			it->second->update(dt);
		}
	}

	Parser* Parser::getTargetObjectAtName(const char * Name){
		DisplayList& displayList = *(getDisplayList());

		if(Name && strcmp(Name,"") != 0 )
		{
			display_list_type::iterator it = displayList.list.begin();
			for (; it != displayList.list.end(); ++it ){
				if( it->second &&									// test 1 : object is not null
					it->second->getName() &&						// test 2 : object has name
					strcmp(it->second->getName(), Name) == 0 &&		// test 3 : name are equal
					it->second->getType() == eTimelineObject ){		// test 4 : object is a timeline object (so subclass of parser )
					TimelineObject* _obj = (TimelineObject*)it->second;
					return ((Parser*)_obj);
				}
			}
		}
		return NULL;
	}

	Parser* Parser::getTargetObjectAtPath(const char * Path){
		if(!Path)
			return NULL;

		Parser* obj = NULL;
		if(Path[0] == '/'){
			Path = &(Path[1]);
			obj = getRoot();
		}
		else{
			obj = this;
		}

		CStrTokenIterator tokenizer( Path, '/' );
		while( ! tokenizer.IsEnd() && obj != NULL ){
			if( strcmp( tokenizer.GetToken(), ".." ) == 0 ){
				obj = obj->getParent();
			}
			else{
				obj = obj->getTargetObjectAtName( tokenizer.GetToken() );
			}
			tokenizer.Next();
		}

		return obj;
	}

	void Parser::doActionList(){
		As2VM& as2VM = *(getAs2VM());
		ActionList& actionList = *(getActionList());
		Parser* target = this;
		ActionRecord action;

		for (uint32 i=0; i<actionList.size(); i++){
			ActionRecordList& CurrentActionList = *(actionList[i]);
			
			action.readFromStream( &CurrentActionList );

			for( ; 
				action.getActionCode() != eActionEnd; 
				action.readFromStream( &CurrentActionList ) )
			{
				switch( action.getActionCode() ){
				// SWF 3 Action Model
				case eActionPlay:				{	target->play();										}break;
				case eActionStop:				{	target->stop();										}break;
				case eActionNextFrame:			{	target->gotoFrame( getActualFrame() + 1 );			}break;
				case eActionPreviousFrame:		{	target->gotoFrame( getActualFrame() - 1 );			}break;

				case eActionGotoFrame:{
					uint16 frame = action.m_Data.read_u16() + 1;
					target->gotoFrame( frame );
				}break;

				case eActionGoToLabel:{
						const char* label = "";
						string_readFromString(label,&action.m_Data);
						target->gotoLabel(label);
					}break;

				case eActionWaitForFrame:
				case eActionGetURL:
				case eActionStopSounds:
				case eActionToggleQuality:
					// skip
					break;

				case eActionSetTarget:{
						const char* targetName = "";
						string_readFromString(targetName,&action.m_Data);
						Parser* newTarget = getTargetObjectAtPath( targetName );
						if(newTarget)	{ target = newTarget;	}
						else			{ target = this;		}
					}break;

				// SWF 4 Action Model
				case eActionPush:
					{
						while( !action.m_Data.isFinish() ){
							uint8 type = action.m_Data.read_u8();
							if( type == 0 ){ // STRING
								const char* str;
								string_readFromString(str, &action.m_Data );
								as2VM.push_str( str );
							}
							else if( type == 1 ){ // FLOAT
								as2VM.push_float( action.m_Data.read_float() );
							}
							else if( type == 2 || type == 3 ){ // Null or undef
								type == 3 ? as2VM.push_undef() : as2VM.push_null();
							}
							else if( type == 4 ){ // register
								as2VM.push_register( action.m_Data.read_u8() );
							}
							else if( type == 5 ){ // bool
								as2VM.push_bool( action.m_Data.read_u8() ? true : false );
							}
							else if( type == 6 ){ // Double
								as2VM.push_double( action.m_Data.read_double() );
							}
							else if( type == 7 ){ // int
								as2VM.push_int( action.m_Data.read_u32() );
							}
							else if( type == 8 ||  type == 9 ){ // constant
								as2VM.push_constant( type == 8 ? action.m_Data.read_u8() : action.m_Data.read_u16() );
							}
						}
					}break;

				case eActionPop:					{	as2VM.pop();					}break;
				case eActionAdd:					{	as2VM.DoAdd();					}break;
				case eActionSubtract:				{	as2VM.DoSub();					}break;
				case eActionMultiply:				{	as2VM.DoMult();					}break;
				case eActionDivide:					{	as2VM.DoDiv();					}break;
				case eActionEquals:					{	as2VM.DoEquals();				}break;
				case eActionLess:					{	as2VM.DoLess();					}break;
				case eActionAnd:					{	as2VM.DoAnd();					}break;
				case eActionOr:						{	as2VM.DoOr();					}break;
				case eActionNot:					{	as2VM.DoNot();					}break;
				case eActionStringEquals:			{	as2VM.DoStringEquals();			}break;
				case eActionStringLength:			{	as2VM.DoStringLength();			}break;
				case eActionStringAdd:				{	as2VM.DoStringAdd();			}break;
				case eActionStringExtract:			{	as2VM.DoStringExtract();		}break;
				case eActionStringLess:				{	as2VM.DoStringLess();			}break;
				case eActionMBStringLength:			{	as2VM.DoMBStringLength();		}break;
				case eActionMBStringExtract:		{	as2VM.DoMBStringExtract();		}break;
				case eActionToInteger:				{	as2VM.DoToInteger();			}break;
				case eActionCharToAscii:			{	as2VM.DoCharToAscii();			}break;
				case eActionAsciiToChar:			{	as2VM.DoAsciiToChar();			}break;
				case eActionMBCharToAscii:			{	as2VM.DoMBCharToAscii();		}break;
				case eActionMBAsciiToChar:			{	as2VM.DoMBAsciiToChar();		}break;
			
				case eActionJump:{
					 int16 offset = action.m_Data.read_s16();
					 if(offset != 0){
						 CurrentActionList.gotoOffset( CurrentActionList.getCurrentOffset() + offset );
					 }
				}break;

				case eActionIf:{
					int16 offset = action.m_Data.read_s16();
					As2Var condition = as2VM.pop();
					if( condition.getAsBool() && offset != 0 ){
						CurrentActionList.gotoOffset( CurrentActionList.getCurrentOffset() + offset );
					}
				}break;

				case eActionCall:{
					As2Var frame_or_label = as2VM.pop();
					// don't support it yet ...
					assert(0);
				}break;

				case eActionGetVariable:{
					As2Var name = as2VM.pop();
					As2Var value;
					Parser* localTarget = target;
					const char* _name = name.getAsString();

					CStrTokenIterator tokenizer(_name, ':');
					tokenizer.Next();
					if( tokenizer.IsEnd() ){ // not find : in str, directly get value
						value = localTarget->getVar( _name );
					}
					else{ // : was find, so get local target first 
						tokenizer.Reset();
						localTarget = localTarget->getTargetObjectAtPath( tokenizer.GetToken() );
						if(localTarget){
							tokenizer.Next();
							value = localTarget->getVar( tokenizer.GetToken() );
						}
					}
					as2VM.push_As2Var( value );
				}break;

				case eActionSetVariable:{
					As2Var value = as2VM.pop();
					As2Var name = as2VM.pop();
					Parser* localTarget = target;
					const char* _name = name.getAsString();

					CStrTokenIterator tokenizer(_name, ':');
					tokenizer.Next();
					if( tokenizer.IsEnd() ){ // not find : in str, directly set value
						localTarget->setVar( _name, value );
					}
					else{ // : was find, so get local target first 
						tokenizer.Reset();
						localTarget = localTarget->getTargetObjectAtPath( tokenizer.GetToken() );
						if(localTarget){
							tokenizer.Next();
							localTarget->setVar( tokenizer.GetToken(), value );
						}
					}
				}break;

				case eActionGetURL2:{
					uint8 SendVarsMethod = action.m_Data.read_uint(2);
					uint8 Reserved = action.m_Data.read_uint(4);
					uint8 LoaddTargetFlag = action.m_Data.read_uint(1);
					uint8 LoaddVariablesFlag = action.m_Data.read_uint(1);
					As2Var value = as2VM.pop();
					// don't support it yet ...
					assert(0);
				}break;

				case eActionGoToFrame2:{
					uint8 Reserved = action.m_Data.read_uint(6);
					bool scene_bias_flag = action.m_Data.read_bool();
					bool play_flag = action.m_Data.read_bool();
					uint16 scene_bias = scene_bias_flag ? action.m_Data.read_u16() : 0;
					As2Var frame_or_label = as2VM.pop();

					if( frame_or_label.getType() == eString ){
						Parser* local_target = target;
						CStrTokenIterator tokenizer(frame_or_label.getAsString(), ':');

						if( isStrContainTarget( frame_or_label.getAsString() ) ){ // is label or number with a target
							// update local target and set frame_or_label value after the ':' in the string
							local_target = local_target->getTargetObjectAtPath( tokenizer.GetToken() );
							tokenizer.Next();
							frame_or_label.set_str( tokenizer.GetToken() ); 
						}

						if(local_target){ // is pure label or number as string
							int str_to_number = frame_or_label.getAsInt();
							if( str_to_number != 0 ){ // is a frame number contain in a string without target
								str_to_number += scene_bias + 1;
								local_target->gotoFrame( (uint16)str_to_number );
								play_flag ? local_target->play() : local_target->stop();
							}
							else{ // should be a label
								bool succeed = local_target->gotoLabel( frame_or_label.getAsString() );
								if(!succeed){ // it was a number equal to zero contain in a string without target !!! seriously FLASH !!!
									str_to_number += scene_bias + 1;
									local_target->gotoFrame( (uint16)str_to_number );
								}
								play_flag ? local_target->play() : local_target->stop();
							}
						}
					}
					else{ // is pure number 
						int frame = frame_or_label.getAsInt();
						frame += scene_bias + 1;
						target->gotoFrame( (uint16)frame );
						play_flag ? target->play() : target->stop();
					}
					
				}break;

				case eActionSetTarget2:{
					As2Var target_name = as2VM.pop();
					Parser* local_target = target->getTargetObjectAtPath( target_name.getAsString() );
					if(local_target)	{target = local_target;} 
					else				{target = this;}
				}break;

				case eActionGetProperty:{
					PropertyEnum propertyIndex = (PropertyEnum) as2VM.pop().getAsInt();
					As2Var target_name = as2VM.pop();
					Parser* local_target = target->getTargetObjectAtPath( target_name.getAsString() );
					if(!local_target){local_target = this;} 
					as2VM.push_As2Var( local_target->getProperty( propertyIndex ) );
				}break;

				case eActionSetProperty:{
					As2Var value = as2VM.pop();
					PropertyEnum propertyIndex = (PropertyEnum) as2VM.pop().getAsInt();
					As2Var target_name = as2VM.pop();
					Parser* local_target = target->getTargetObjectAtPath( target_name.getAsString() );
					if(!local_target){local_target = this;} 
					local_target->setProperty( propertyIndex, value );
				}break;

				case eActionCloneSprite:{
					uint16 depth = (uint16) as2VM.pop().getAsInt();
					As2Var target_name = as2VM.pop();
					As2Var source_name = as2VM.pop();
					Parser* local_source = target->getTargetObjectAtPath( source_name.getAsString() );
					if(!local_source){local_source = this;} 

					DisplayList& displayList = *(target->getDisplayList());
					SceneObject* object = displayList[depth];
					if(object){ target->freeObject( object ); }

					TimelineObject* timeline = target->getNewTimelineObject();
					timeline->cloneFromObject( (TimelineObject*)local_source, source_name.getAsString(), depth, target );
					object = (SceneObject*) timeline;
					displayList[depth] = object;

				}break;

				case eActionRemoveSprite:{	
					As2Var target_name = as2VM.pop();
					Parser* local_target = target->getTargetObjectAtPath( target_name.getAsString() );
					if( local_target && local_target->getParent() ){
						DisplayList& displayList = *(local_target->getParent()->getDisplayList());
						TimelineObject* timeline_target = (TimelineObject*)local_target;
						uint16 depth = timeline_target->GetDepth();

						local_target->getParent()->freeObject( (SceneObject*)timeline_target );

						displayList.list.erase( displayList.list.find(depth) );
					}
				}break;

				case eActionStartDrag:{
					As2Var target_name = as2VM.pop();	
					As2Var lock_center = as2VM.pop();	
					As2Var constrain = as2VM.pop();	
					if(constrain.getAsBool()){
						As2Var y2 = as2VM.pop();
						As2Var x2 = as2VM.pop();
						As2Var y1 = as2VM.pop();
						As2Var x1 = as2VM.pop();
					}
					// don't support it yet ...
					assert(0);
				}break;

				case eActionEndDrag:{
					// don't support it yet ...
					assert(0);
				}break;

				case eActionWaitForFrame2:{
					As2Var target_name = as2VM.pop();	
					uint8 to_skip = action.m_Data.read_u8();
					// I'm not sure about what wait for frame should do !
					// it seems to skip some action in case of the target frame is not loaded, 
					// but in my case, a frame "not loaded" doesn't make sense, all the flash file is in memory,
					// I'm just parsing it...
					// so I guess in my case all frame are loaded, so maybe we should all the time skip the action ?
					assert(0);
				}break;

				case eActionTrace:{
					As2Var toTrace = as2VM.pop();
					printf("Trace : %s\n", toTrace.getAsString() );
				}break;

				case eActionGetTime:{
					clock_t uptime = clock() / (CLOCKS_PER_SEC / 1000);
					as2VM.push_int( (int) uptime );
				}break;

				case eActionRandomNumber:{
					int MAX_VAL = as2VM.pop().getAsInt();
					int rand_number = rand() % MAX_VAL;
					as2VM.push_int( rand_number );
				}break;

				// SWF 5
				// SWF ...
				};
			}
		}

		actionList.clear();
	}


	void Parser::rewind(){
		getParserStream()->rewind();
		setActualFrame( 0 );
	}

	void Parser::ParseNextFrame(){
		Stream* stream = getParserStream();
		ActionList& actionList = *(getActionList());

		if( getActualFrame() > getFrameNumber() ){
			if(getFrameNumber() <= 1){ // if timeline contain only one frame, do not rewind, you will parse the same frame again and again ...
				stop(); 
				return;
			}
			rewind();
		}

		TAG tag;
		do{
			tag.readFromStream( stream );

			if( tag.type == eEnd ){
				if(getFrameNumber() <= 1) { break; }
				rewind();
				tag.readFromStream( stream );
			}

			bool bFounded = false;
			if(!bFounded){ 
				bFounded = Parse_DefineTags( tag );
			}
			if(!bFounded){ 
				bFounded = Parse_DisplayListTags( tag );
			}
			if(!bFounded){ 
				bFounded = Parse_ActionTags( tag );
			}
			if(!bFounded){ 
				bFounded = Parse_ControlTags( tag );
			}
			if(!bFounded){ 
				bFounded = Parse_UnsupportedTags( tag );
			}
			if(!bFounded){ 
				assert(0); 
			}
		} while ( tag.type != eShowFrame && tag.type != eEnd );

		setActualFrame( getActualFrame() + 1 );
	}

	bool Parser::Parse_DisplayListTags( const TAG& tag ){
		switch( tag.type ){
		case ePlaceObject:
			Parse_PlaceObject(tag);
			return true;
		case ePlaceObject2:
			Parse_PlaceObject2(tag);
			return true;
		case eRemoveObject:
		case eRemoveObject2:
			Parse_RemoveObject(tag);
			return true;
		};
		return false;
	}
	bool Parser::Parse_RemoveObject( const TAG& tag ){
		Stream* stream = getParserStream();
		DisplayList& displayList = *(getDisplayList());
		CharacterList& characterList = *(getCharacterList());

		uint32 prev_offset = stream->getCurrentOffset();

		if( tag.type == eRemoveObject ){
			uint16 characterId = stream->read_u16();
		}
		if(tag.type == eRemoveObject2 || tag.type == eRemoveObject ){
			uint16 depth = stream->read_u16();

			SceneObject* object = displayList[depth];
			if(!object){
				printf("Error : try to remove an object at none existing depth : %d\n", depth);
				assert(0);
			}

			if(object){
				object->init(); // reset before free, should be useless But for now the pool cannot reinit the object , so I do it here :)
				freeObject( object );
				object = NULL;
			}

			displayList.list.erase( displayList.list.find(depth) );
		}
		return true;
	}
	bool Parser::Parse_PlaceObject( const TAG& tag ){
		Stream* stream = getParserStream();
		DisplayList& displayList = *(getDisplayList());
		CharacterList& characterList = *(getCharacterList());

		if( tag.type == ePlaceObject ){
			uint32 prev_offset = stream->getCurrentOffset();
			uint16 characterId = stream->read_u16();
			uint16 depth = stream->read_u16();
			MATRIX mat; mat.readFromStream( stream );
			CXFORM color; bool has_cxform = false; 
			if( tag.size > (stream->getCurrentOffset() - prev_offset) ){
				color.readRGBFromStream( stream );
				has_cxform = true;
			}

			Character* character = characterList.getCharacter( characterId );
			if(!character){
				printf("the character[%d] is used before been definded !\n", characterId);
				assert(0);
				return false;
			}

			SceneObject* object = displayList[depth];
			if(!object){
				if(character->getType() == eSpriteCharacter ){
					TimelineObject* timeline = getNewTimelineObject();
					object = (SceneObject*) timeline;
				}
				else{
					object = getNewSceneObject();
				}
			}
			assert( object );
			
			object->SetParent( this );
			object->setCharacter( character );
			object->m_Transform = mat;
			if(has_cxform){
				object->m_ColorTransfom = color;
			}

			displayList[depth] = object;
		}

		return true;
	}
	bool Parser::Parse_PlaceObject2( const TAG& tag ){
		Stream* stream = getParserStream();
		DisplayList& displayList = *(getDisplayList());
		CharacterList& characterList = *(getCharacterList());

		if( tag.type == ePlaceObject2 ){
			uint32 prev_offset = stream->getCurrentOffset();

			bool PlaceFlagHasClipActions = stream->read_bool();
			bool PlaceFlagHasClipDepth = stream->read_bool();
			bool PlaceFlagHasName = stream->read_bool();
			bool PlaceFlagHasRatio = stream->read_bool();
			bool PlaceFlagHasColorTransform = stream->read_bool();
			bool PlaceFlagHasMatrix = stream->read_bool();
			bool PlaceFlagHasCharacter = stream->read_bool();
			bool PlaceFlagMove = stream->read_bool();

			uint16 depth = stream->read_u16();
			uint16 characterId = 0;
			MATRIX trans;
			CXFORM color;
			uint16 MorphRatio = 0;
			const char* name = "";
			uint16 clipDepth = 0;

			if(PlaceFlagHasCharacter){
				characterId = stream->read_u16();
			}
			if(PlaceFlagHasMatrix){
				trans.readFromStream( stream );
			}
			if(PlaceFlagHasColorTransform){
				color.readRGBAFromStream( stream );
			}
			if(PlaceFlagHasRatio){
				MorphRatio = stream->read_u16();
			}
			if(PlaceFlagHasName){
				name = (const char*) stream->getCurrentPosition();
				stream->skip_byte( strlen(name) + 1 );
			}
			if(PlaceFlagHasClipDepth){
				clipDepth = stream->read_u16();
			}
			if( PlaceFlagHasClipActions){
				int TotalTageSizeLessReeded = tag.size - (stream->getCurrentOffset() - prev_offset);
				stream->skip_byte( TotalTageSizeLessReeded );
			}

			Character* character_to_set = NULL;

			SceneObject* object = displayList[depth];
			if( (PlaceFlagMove && PlaceFlagHasCharacter && object) || 
				(PlaceFlagHasCharacter && object && object->getCharacter()->getCharacterId() != characterId) ){
					// reset object and set new character
					Character* character = characterList.getCharacter( characterId );
					if( object->getCharacter() != character ){
						
						if( (character->getType() == eSpriteCharacter && object->getType() != eTimelineObject) ||
							(character->getType() != eSpriteCharacter && object->getType() == eTimelineObject) ){
							// sceneObject / Character type are not compatible, destroy and re create correct one 
							freeObject( object );
							if(character->getType() == eSpriteCharacter ){
								TimelineObject* timeline = getNewTimelineObject();
								object = (SceneObject*) timeline;
							}
							else{
								object = getNewSceneObject();
							}
						}
						
						object->SetParent( this );
						character_to_set = character;
						object->m_Depth = depth;
						displayList[depth] = object;
					}
			}
			else if(PlaceFlagHasCharacter && !object){
				// create new object
				Character* character = characterList.getCharacter( characterId );
				if(character->getType() == eSpriteCharacter ){
					TimelineObject* timeline = getNewTimelineObject();
					object = (SceneObject*) timeline;
				}
				else{
					object = getNewSceneObject();
				}

				object->SetParent( this );
				character_to_set = character;
				object->m_Depth = depth;
				displayList[depth] = object;
			}


			// set value
			if(PlaceFlagHasMatrix && ! object->m_LockByActionScript ){
				object->m_Transform = trans;
			}
			if(PlaceFlagHasColorTransform && ! object->m_LockByActionScript ){
				object->m_ColorTransfom = color;
			}
			if(PlaceFlagHasRatio){
				object->m_MorphRatio = MorphRatio;
			}
			if(PlaceFlagHasName){
				object->m_Name = name;
			}
			if(PlaceFlagHasClipDepth){
				object->m_ClipDepth = clipDepth;
			}
			if( PlaceFlagHasClipActions){
				// TODO
			}

			if(character_to_set){ // do first init like ParseFirstFrame or CollectFrameLabels
				object->setCharacter( character_to_set ); 
			}
		}
		return true;
	}

	bool Parser::Parse_ControlTags( const TAG& tag ){
		return false;
	}

	bool Parser::Parse_DefineTags( const TAG& tag ){
		switch( tag.type ){
		case eDefineShape:
		case eDefineShape2:
		case eDefineShape3:
		case eDefineShape4:
			Parse_DefineShape(tag);
			return true;

		case eDefineSprite:
			Parse_DefineSprite(tag);
			return true;

		case eDefineBitsLossLess:
		case eDefineBitsLossLess2:
			Parse_DefineBitmap(tag);
			return true;

		case eDefineFont:
		case eDefineFont2:
			return Parse_DefineFont( tag );
		case eDefineFontInfo:
		case eDefineFontInfo2:
			return Parse_DefineFontInfo( tag );

		case eDefineText:
		case eDefineText2:
			return Parse_DefineText1( tag );
		};
		return false;
	}
	bool Parser::Parse_DefineShape( const TAG& tag ){
		Stream* stream = getParserStream();
		DisplayList& displayList = *(getDisplayList());
		CharacterList& characterList = *(getCharacterList());

		if( tag.type == eDefineShape || tag.type == eDefineShape2 || tag.type == eDefineShape3 || tag.type == eDefineShape4 ){

			uint16 characterId = stream->read_u16();

			if ( characterList.getCharacter(characterId) == NULL ){

				Shape* shape = characterList.createShapeDef();
				uint32 prevOff = stream->getCurrentOffset();
				shape->readFromStream( stream, tag.type, &characterList );
				shape->setCharacterId(characterId);

				characterList.StoreShapeInDictionary( shape );
			}
			else{
				stream->skip_byte( tag.size - 2 );
			}
		}

		return true;
	}
	bool Parser::Parse_DefineSprite( const TAG& tag ){
		Stream* stream = getParserStream();
		DisplayList& displayList = *(getDisplayList());
		CharacterList& characterList = *(getCharacterList());

		if( tag.type == eDefineSprite ){

			uint16 characterId = stream->read_u16();

			if ( characterList.getCharacter(characterId) == NULL ){

				SPRITE_DEFINITION* sprite = characterList.createSpriteDef();
				uint32 prevOff = stream->getCurrentOffset();
				sprite->readFromStream( stream, tag );
				sprite->m_CharacterId = characterId;

				characterList.StoreSpriteInDictionary( sprite );
			}
			else{
				stream->skip_byte( tag.size - 2 );
			}
		}
		return true;
	}

	bool Parser::Parse_DefineBitmap( const TAG& tag ){
		Stream* stream = getParserStream();
		DisplayList& displayList = *(getDisplayList());
		CharacterList& characterList = *(getCharacterList());

		if( tag.type == eDefineBitsLossLess || 
			tag.type == eDefineBitsLossLess2 ){

			uint16 characterId = stream->read_u16();

			if ( characterList.getCharacter(characterId) == NULL ){

				Bitmap* bitmap = characterList.createBitmapDef();
				uint32 prevOff = stream->getCurrentOffset();
				bitmap->readFromStream( stream, tag, getRenderer() );
				bitmap->setCharacterId(characterId);

				characterList.StoreBitmapInDictionary( bitmap );
			}
			else{
				stream->skip_byte( tag.size - 2 );
			}
		}
		return true;
	}


	bool Parser::Parse_DefineFont( const TAG& tag )
	{
		Stream* stream = getParserStream();
		DisplayList& displayList = *(getDisplayList());
		CharacterList& characterList = *(getCharacterList());

		if( tag.type == eDefineFont || 
			tag.type == eDefineFont2 ){
			uint16 characterId = stream->read_u16();

			if ( characterList.getCharacter(characterId) == NULL ){

				Font* font = characterList.createFontDef();
				uint32 prevOff = stream->getCurrentOffset();
				font->readFromStream( stream, tag, &characterList );
				font->setCharacterId(characterId);

				characterList.StoreFontInDictionary( font );
			}
			else{
				stream->skip_byte( tag.size - 2 );
			}
		}

		return true;
	}


	bool Parser::Parse_DefineFontInfo( const TAG& tag )
	{
		Stream* stream = getParserStream();
		DisplayList& displayList = *(getDisplayList());
		CharacterList& characterList = *(getCharacterList());

		if( tag.type == eDefineFontInfo || 
			tag.type == eDefineFontInfo2 ){
			uint16 characterId = stream->read_u16();

			Font* pFont = characterList.getFont(characterId);
			if ( pFont ){
				pFont->getEditableFontInfo().readFromStream( stream, tag, pFont->getGlyphNumber() );
			}
			else{
				stream->skip_byte( tag.size - 2 );
			}
		}

		return true;
	}



	bool Parser::Parse_DefineText1( const TAG& tag )
	{
		Stream* stream = getParserStream();
		DisplayList& displayList = *(getDisplayList());
		CharacterList& characterList = *(getCharacterList());

		if( tag.type == eDefineText ||
			tag.type == eDefineText2 ){

			uint16 characterId = stream->read_u16();
		
			if ( characterList.getCharacter(characterId) == NULL ){

				StaticText* text = characterList.createStaticTextDef();
				uint32 prevOff = stream->getCurrentOffset();
				text->readFromStream( stream, tag, &characterList );
				text->setCharacterId(characterId);

				characterList.StoreStaticTextInDictionary( text );
			}
			else{
				stream->skip_byte( tag.size - 2 );
			}


			return true;
		}
		return false;
	}


	bool Parser::Parse_ActionTags( const TAG& tag ){
		switch( tag.type ){
		case eDoAction:
			return Parse_DoAction(tag);
		};
		return false;
	}

	bool Parser::Parse_DoAction( const TAG& tag ){
		Stream* stream = getParserStream();
		DisplayList& displayList = *(getDisplayList());
		CharacterList& characterList = *(getCharacterList());
		ActionList& actionList = *(getActionList());

		int prev_offset = stream->getCurrentOffset();

		if( tag.type == eDoAction ){
			actionList.push_back( stream->getCurrentPosition(), tag.size );
			stream->skip_byte( tag.size );
		}

		int readed_byte = stream->getCurrentOffset() - prev_offset;
		assert( tag.size == readed_byte );

		return true;
	}

	bool Parser::Parse_UnsupportedTags( const TAG& tag ){
		Stream* stream = getParserStream();
		// skip it
		stream->skip_byte( tag.size );
		return true;
	}

	// as2 function

	void Parser::gotoFrame( uint16 frame ){
		if(frame == 0 || frame > getFrameNumber() )
			return; // impossible gotoFrame, exit

		if( getActualFrame() > frame ){
			rewind(); // if we go back in the timeline - rewind
		}

		while( getActualFrame() < frame ){
			ParseNextFrame(); // parse until the correct frame is reached
		}
	}

	bool Parser::gotoLabel( const char* label ){
		uint16 frame = getFrameForLabel(label);
		gotoFrame( frame );
		return frame != 0;
	}

	Parser* Parser::getRoot(){
		Parser* _root = this;
		while( _root->getParent() ){
			_root = _root->getParent();
		}
		return _root;
	}

	bool Parser::isStrContainTarget( const char * Str ){
		return strpbrk(Str,":") != NULL;
	}
};